package sundial;


/**
 * @author Alan Sheen
 * 
 * modified by
 * @author Takanori Shiotsu
 * @author Reyn Matsumoto
 * 
 * This class computes the EOT and
 * the hour line angles in degrees (with option to convert to radians)
 * 
 * double[] angOfHours;
 * 	Stores the angles made between the hour lines and the gnomon
 * 	  position 0  = 6  am
 *
 *
 * int[] days
 *  Stores the days in each month
 *    position 0 =  January
 *    position 11 = December
 */

public class SundialCompute{

	private double   latitude;
	private double   longitude;
	private Double[] angOfHours;
	private int      stdMeri;
	private int[]    days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	private int      month;
	private int      day;
	private int      year;
	private boolean	 dls;
	
	public SundialCompute(double lati, double longi, int month, int day, int year, boolean dls){
		latitude   = lati;
		longitude  = longi;
		angOfHours = new Double[6];
		stdMeri    = (int)(Math.round(longitude/15) * 15);
		this.month = month;
		this.day   = day;
		this.year  = year;
		this.dls   = dls;
	}
	
	/**
	 * Calculates the angle formed by each hour line with the gnomon
	 *
	 *
	 * tan(d) = tan(t)*sin(phi)
	 * phi is the latitude of the place the sundial is located
	 * d is the angle which the hour line makes with the gnomon
	 * t is the time measured from noon in degrees of arc. 1 hour = 15 degrees
	 *
	 * Also adjusts the angle based on the longitude and standard meridian
	 * Returns the calculated angles in a double[]
	 * 
	 */
	public Double[] hourAngles(){
		double tanD          = 0;
		double hourOfTime    = 15.00;
		double adjustmentAng = 0;
		
		/*
		 * Adjust hour angle by the difference of the longitude
		 * and respective standard meridian
		 *
		 * adjustmentAng will be positive if longitude is west of
		 * standard meridian and negative otherwise
		 */
		if(longitude == stdMeri){
			adjustmentAng = 0;
		}
		else {
			adjustmentAng = stdMeri - longitude;
		}

		// Compute EOT
		adjustmentAng = adjustmentAng - EOT();		 

		/*
		 * Compute angles for 6, 7, 8, 9, 10, 11
		 * 
		 */
		for(int i = -6; i < 0; i++){
			if(Math.abs(i*hourOfTime) == 90){
				angOfHours[i+6] = 90.0;
			}
			else{
				tanD = Math.tan(((Math.abs(i)*hourOfTime+adjustmentAng)/180)*Math.PI)*Math.sin((Math.abs(latitude)/180)*Math.PI);
				angOfHours[i+6] = Math.toDegrees(Math.atan(tanD));
			}
		}

		return angOfHours;
	}	
	
	/**
	 * Accepts a double[] of angles in degrees
	 * Returns the double[] of angles in radians 
	 */
	public Double[] intoRadians(Double[] angles){
		for(int i = 0; i < angles.length; i++){
			angles[i] = Math.toRadians(angles[i]);
		}
		return angles;
	}
	
	/**
	  * Further refine adjustmentAng based on EOT
	  * This is done to correct for the Earth's non-circular orbit 
	  * Sundials are "slow" in Jan, Feb, Mar, Apr, July, Sep
	  *              "fast" in May, June, Oct, Nov, Dec 
	  *
	  * E = 9.87 * sin (2B) - 7.53 * cos (B) - 1.5 * sin (B)
	  * B = 360 * (N - 81) / 365
	  * N = day number, January 1 = day 1
	  *
	  * The day number can change based on leap year
	  */
	public double EOT(){
		int dayNum = 0;
		boolean isLeapY = false;
		// B and E are part of the EOT equation
		double B = 0;
		double E = 0;
		
		// Find the number of days that have past
		for(int i = 0; i < month-1; i++){
			dayNum = dayNum + days[i];
		}
		dayNum = dayNum + day;
		
		// Determine if there is a leap year
		if(year%4 == 0){
			if(year%100 != 0){
				isLeapY = true;
			}
			else if(year%400 == 0){
				isLeapY = true;
			}
		}
		
		// Add an extra day if it is past February on a leap year
		if(isLeapY && month > 2){
			dayNum = dayNum + 1;
		}
		
		/* 
		 * Calculate time adjustment based on dayNum
		 * 
		 * Convert B to radians
		 * E/4 = translate minutes to degrees (4 minute = 1 degree)
		 */
		B = 360.0 * (dayNum - 81.0) / 365.0;
		B = (B/180.0)*Math.PI;
		E = 9.87*Math.sin(2*B) - 7.53*Math.cos(B) - 1.5*Math.sin(B);
		E /= 4.0;
		
		return E;
				
	}	
	
	public void printAngles(){
		for(int i = 0; i < 6; i++){
			System.out.println(i+6 + ": " + angOfHours[i]);
		}
	}
	
	//from gnomon
	public static double getOpposite(double latitude) {
    	
    	//convert to radians from degrees/ get latitude
    	latitude = Math.toRadians(latitude);
    	
    	double opposite;
    	
    	opposite = Math.tan(latitude) * 400;
    	
    	//accounting for 100 extra from left
    	opposite += 100;
    	
    	return opposite;	
    }
	
	//from basicpanel
	public static double calculateOpp(double degree, double width)
    {
    	degree = Math.toRadians(degree);
    	double yCoor;
    	yCoor = Math.tan(degree) * (width/2);
    	return yCoor;
    }
    
	//from basicpanel
    public static double calculateAdj(double degree, double newYCoor)
    {
    	degree = Math.toRadians(degree);
    	double xCoor;
    	xCoor = Math.tan(degree) / newYCoor;
    	xCoor = Math.pow(xCoor, -1);
    	
    	return xCoor;
    }
	
	public double getLatitude() {
		return Math.abs(latitude);
	}

	public void setLatitude(double latitude) {
		this.latitude = latitude;
	}

	public double getLongitude() {
		return longitude;
	}

	public void setLongitude(double longitude) {
		this.longitude = longitude;
	}

	public Double[] getAngOfHours() {
		return angOfHours;
	}

	public void setAngOfHours(Double[] angOfHours) {
		this.angOfHours = angOfHours;
	}

	public int getStdMeri() {
		return stdMeri;
	}

	public void setStdMeri(int stdMeri) {
		this.stdMeri = stdMeri;
	}

	public int[] getDays() {
		return days;
	}

	public void setDays(int[] days) {
		this.days = days;
	}

	public int getMonth() {
		return month;
	}

	public void setMonth(int month) {
		this.month = month;
	}

	public int getDay() {
		return day;
	}

	public void setDay(int day) {
		this.day = day;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}
	
	//
	public boolean getDLS() {
		return dls;
	}
	
	public void setDLS(boolean dls) {
		this.dls = dls;
	}
}